package pl.asoft.nbp.dao.impl;

import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.springframework.transaction.annotation.Transactional;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test;

import pl.asoft.nbp.dao.ExchangeRateDAO;
import pl.asoft.nbp.model.Currency;
import pl.asoft.nbp.model.ExchangeRate;

import javax.validation.ConstraintViolationException;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

import static org.testng.Assert.*;
import static pl.asoft.nbp.dao.QueryParameterBuilder.parameter;

@Test
@ContextConfiguration(locations = { "classpath:spring-test-config.xml" })
public class ExchangeRateDAOImplTestIT extends AbstractTestNGSpringContextTests  {

    private static final double AVG_USD_IN_DB = 3.0154954581673308;

	@Autowired
    ExchangeRateDAO exchangeRateDAO;

    private ExchangeRate dummy = new ExchangeRate();

    @BeforeClass
    public void beforeClass(){
        Currency currency = new Currency();
        currency.setName("test name");
        currency.setShortName("XDR");
        currency.setConversion(1);
        dummy.setPrice(BigDecimal.valueOf(2.50));
        dummy.setDateOfPublication(date("2005-01-05"));
        dummy.setCurrency(currency);
    }
    
//    @Test
//    public void shouldUpdate() {
//    	exchangeRateDAO.save(dummy);
//
//    	dummy.getCurrency().setName("nowa");
//
//        //Dwie osobne transakcje, dlatego dwa razy poszedl w logach update
//    	exchangeRateDAO.update(dummy);
//
//    }

    @Test
    public void testFindByNotExistingShortCode() {
        List<ExchangeRate> exchangeRates = exchangeRateDAO.findByShortName("asdasd");
        assertTrue(exchangeRates.isEmpty());
    }



    @Test
    public void testFindAllBelowAverage() {
        int exchangeRateBelowAverageInDB = 999;
        double averageDB = 3.0154954581673308;

        List<ExchangeRate> exchangeRates = exchangeRateDAO.findAllBelowAverage("USD");

        assertTrue(!exchangeRates.isEmpty());
        assertEquals(exchangeRates.size(), exchangeRateBelowAverageInDB);
        assertTrue(exchangeRates.get(0).getPrice().doubleValue() < averageDB);
    }

    @Test
    public void testFindAllAboveAverage() {
        int exchangeRateSizeInDB = 1511;

        List<ExchangeRate> exchangeRates = exchangeRateDAO.findAllAboveAverage("USD");

        assertTrue(!exchangeRates.isEmpty());
        assertEquals(exchangeRates.size(), exchangeRateSizeInDB);
        assertTrue(exchangeRates.get(0).getPrice().doubleValue() > AVG_USD_IN_DB);
    }

    @Test
    public void testFindById() {
        ExchangeRate exchangeRate = exchangeRateDAO.find(1l);
        assertNotNull(exchangeRate);
    }

    @Test
    public void testFindByShortName() {
        int size = exchangeRateDAO.findByNamedQuery(exchangeRateDAO.FIND_BY_SHORT_NAME,
                parameter("shortName", "USD").build()).size();

        assertTrue(size > 0);
    }

    @Test
    public void testFindByNamedQueryWithMapAndResultLimit() {
        int expectedRowSize = 10;

        int size = exchangeRateDAO.findByNamedQuery(exchangeRateDAO.FIND_BY_SHORT_NAME,
                parameter("shortName", "USD").build(),expectedRowSize).size();

        assertTrue(size == expectedRowSize);
    }

    @Test
    public void testFindByDateBetween() {
        int exchangeRateSizeDB = 48;

        List<ExchangeRate> exchangeRates = exchangeRateDAO.findAllByDateBetween("ALL",date("2005-01-03"),date("2005-01-04"));

        assertEquals(exchangeRates.size(), exchangeRateSizeDB);
    }

    @Test
    public void testFindByDate() {

        int exchangeRateSizeDB = 24;

        List<ExchangeRate> exchangeRates = exchangeRateDAO.findByDate(date("2005-01-03"));

        assertEquals(exchangeRates.size(), exchangeRateSizeDB);
    }



    @Test
    public void testFindByShortNameAndDate(){

        ExchangeRate exchangeRate = exchangeRateDAO.findByShortNameAndDate("USD", date("2005-01-05"));

        assertNotNull(exchangeRate);
        assertTrue(exchangeRate.getCurrency().getShortName().equals("USD"));
        assertEquals(exchangeRate.getDateOfPublication(), date("2005-01-05"));
    }

    @Test
    public void testFindExchangeRateWithMaxPrice(){
        double priceDB = 3.8978;

        ExchangeRate er = exchangeRateDAO.findExchangeRateWithMaxPrice("USD").get(0);

        assertNotNull(er);
        assertEquals(er.getDateOfPublication(), date("2009-02-18"));
        assertEquals(er.getPrice().doubleValue(), priceDB);
    }

    @Test
    public void testFindExchangeRateWithMinPrice(){
        double priceDB = 2.022;

        ExchangeRate er = exchangeRateDAO.findExchangeRateWithMinPrice("USD").get(0);

        assertNotNull(er);
        assertEquals(er.getDateOfPublication(),date("2008-07-21"));
        assertEquals(er.getPrice().doubleValue(), priceDB);
    }

    @Test
    public void testFindExchangeRateWithMinPriceLimit(){
        int limit = 5;
        double priceDB = 2.022;

        List<ExchangeRate> exchangeRates = exchangeRateDAO.findAllWithMinPriceLimit("USD", limit);

        assertTrue(!exchangeRates.isEmpty());
        assertEquals(exchangeRates.size(), limit);
        assertEquals(exchangeRates.get(0).getPrice().doubleValue(), priceDB);
    }

    @Test
    public void testFindAllWithMaxPriceLimit(){
        double priceDB1 = 3.8978;
        double priceDB2 = 3.8238;
        int limit = 5;

        List<ExchangeRate> exchangeRates = exchangeRateDAO.findAllWithMaxPriceLimit("USD", limit);

        assertEquals(exchangeRates.size(), limit);
        assertEquals(exchangeRates.get(0).getPrice().doubleValue(), priceDB1);
        assertEquals(exchangeRates.get(1).getPrice().doubleValue(), priceDB2);
    }

    @Test
    public void testFindExchangeRateWithMaxPriceAndDateBetween(){
        double priceDB = 3.0406;

        ExchangeRate er = exchangeRateDAO.findExchangeRateWithMaxPrice("USD",
                                           date("2005-01-03"),
                                           date("2005-01-04")).get(0);

        assertNotNull(er);
        assertEquals(er.getDateOfPublication(),date("2005-01-04"));
        assertEquals(er.getPrice().doubleValue(), priceDB);
    }

    @Test
    public void testFindExchangeRateWithMinPriceAndDateBetween(){

        double priceDB = 3.0123;

        ExchangeRate er = exchangeRateDAO.findExchangeRateWithMinPrice("USD",
                                          date("2005-01-03"),
                                          date("2005-01-04")).get(0);
        
        assertNotNull(er);
        assertEquals(er.getDateOfPublication(),date("2005-01-03"));
        assertEquals(er.getPrice().doubleValue(), priceDB);
    }

    @Test
    public void testFindAll() {
        List<ExchangeRate> exchangeRates = exchangeRateDAO.findAll();

        assertTrue(exchangeRates.size() > 0);
    }

    @Test
    public void testWithMaxPriceAndDateBetween(){
        //given
        double expectedMaxPriceDB = 9.5349;

        //when
        ExchangeRate er = exchangeRateDAO.findExchangeRateWithMaxPrice(
                date("2005-01-03"),
                date("2005-01-11"));
        //then
        assertNotNull(er);
        assertEquals(er.getDateOfPublication(),date("2005-01-05"));

        assertEquals(er.getPrice().doubleValue(), expectedMaxPriceDB);
    }

    @Test
    @BeforeGroups(groups = "saveOperation")
    public void testFindExchangeRateAverageBetweenTwoDates(){
        double avgDB = 3.02645;

        double avg = exchangeRateDAO.findExchangeRateAverage("USD",date("2005-01-03"),
                date("2005-01-04"));

        assertEquals(avg, avgDB);
    }

    @Test
    @BeforeGroups(groups = "saveOperation")
    public void testFindExchangeRateAverage(){
        //given
        double avgDB = 3.0154954581673308;

        //when
        double avg = exchangeRateDAO.findExchangeRateAverage("USD");

        //then
        assertEquals(avg, avgDB);
    }

    //this test must be run after avg test
    @Transactional
    @Test(groups = "saveOperation")
    public void testSaveExchangeRate() {
        exchangeRateDAO.save(dummy);
        assertTrue(dummy.getId() != null);
        exchangeRateDAO.delete(dummy.getId());
    }

    @Transactional
    @Test(groups = "saveOperation", expectedExceptions = ConstraintViolationException.class)
    public void testSaveExchangeRateWithNullDate() throws ConstraintViolationException{
        dummy.setDateOfPublication(null);
        exchangeRateDAO.save(dummy);
    }

    private Date date(String string) {
        return DateTime.parse(string).toDate();
    }

}